package com.tau.iplayer.client;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.Map.Entry;

import com.bramosystems.oss.player.core.client.ConfigParameter;
import com.bramosystems.oss.player.core.client.LoadException;
import com.bramosystems.oss.player.core.client.PlayException;
import com.bramosystems.oss.player.core.client.PluginNotFoundException;
import com.bramosystems.oss.player.core.client.PluginVersionException;
import com.bramosystems.oss.player.core.client.TransparencyMode;
import com.bramosystems.oss.player.core.event.client.PlayStateEvent;
import com.bramosystems.oss.player.core.event.client.PlayStateHandler;
import com.bramosystems.oss.player.youtube.client.ChromelessPlayer;
import com.bramosystems.oss.player.youtube.client.PlaybackQuality;
import com.bramosystems.oss.player.youtube.client.PlayerParameters;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseUpEvent;
import com.google.gwt.event.dom.client.MouseUpHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.gadgets.client.DynamicHeightFeature;
import com.google.gwt.gadgets.client.Gadget;
import com.google.gwt.gadgets.client.NeedsDynamicHeight;
import com.google.gwt.gadgets.client.NeedsSetPrefs;
import com.google.gwt.gadgets.client.SetPrefsFeature;
import com.google.gwt.gadgets.client.Gadget.AllowHtmlQuirksMode;
import com.google.gwt.gadgets.client.Gadget.ModulePrefs;
import com.google.gwt.gadgets.client.Gadget.UseLongManifestName;
import com.google.gwt.gadgets.client.gwtrpc.GadgetsGwtRpc;
import com.google.gwt.gadgets.client.osapi.Callback;
import com.google.gwt.gadgets.client.osapi.NeedsOsapi;
import com.google.gwt.gadgets.client.osapi.OsapiCollection;
import com.google.gwt.gadgets.client.osapi.OsapiError;
import com.google.gwt.gadgets.client.osapi.OsapiFeature;
import com.google.gwt.gadgets.client.osapi.people.PeopleService;
import com.google.gwt.gadgets.client.osapi.people.Person;
import com.google.gwt.http.client.URL;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONValue;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.ServiceDefTarget;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CellPanel;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.DecoratedTabPanel;
import com.google.gwt.user.client.ui.DisclosurePanel;
import com.google.gwt.user.client.ui.DockPanel;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.FocusPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.MenuBar;
import com.google.gwt.user.client.ui.MultiWordSuggestOracle;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.SuggestBox;
import com.google.gwt.user.client.ui.TabPanel;
import com.google.gwt.user.client.ui.ToggleButton;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.widgetideas.client.ProgressBar;
import com.tau.iplayer.client.algorithms.AverageRatingsCalculatorStrategy;
import com.tau.iplayer.client.algorithms.BayesianAverageRatingCalc;
import com.tau.iplayer.client.dialogs.AddPlaylistDialog;
import com.tau.iplayer.client.dialogs.AddTrackDialog;
import com.tau.iplayer.client.dialogs.DeletePlaylistDialog;
import com.tau.iplayer.client.dialogs.HelpDialog;
import com.tau.iplayer.client.dialogs.IncomingTrackDialog;
import com.tau.iplayer.client.dialogs.MoveTrackDialog;
import com.tau.iplayer.client.dialogs.RenamePlaylistDialog;
import com.tau.iplayer.client.dialogs.RenameTrackDialog;
import com.tau.iplayer.client.dialogs.SendTrackDialog;
import com.tau.iplayer.client.models.Playlist;
import com.tau.iplayer.client.models.Track;
import com.tau.iplayer.client.models.TrackTransaction;
import com.tau.iplayer.client.resources.IPlayerResources;
import com.tau.iplayer.client.widgets.RatingViewer;
import com.tau.iplayer.client.widgets.ThumbnailTrackDisplay;
import com.tau.iplayer.client.widgets.TrackDisplay;

/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */
@ModulePrefs(title = "iPlayer", author = "Erez Kastel, Shay Sheinfeld", description = "Listen, manage and share your favourite music - anytime, anywhere!", thumbnail = "http://tau-iplayer.googlecode.com/files/thumbnail.png", screenshot = "http://tau-iplayer.googlecode.com/files/gwt_screenshot2.PNG", author_email = "tau.iplayer@gmail.com")
@UseLongManifestName(false)
@AllowHtmlQuirksMode(false)
public class IPlayer extends Gadget<IPlayerPreferences> implements
		NeedsDynamicHeight, NeedsOsapi, NeedsSetPrefs {

	private static final int MAX_TOP_TRACKS = 20;

	private DynamicHeightFeature dynamicHeightFeature;

	private IPlayerResources resources = GWT.create(IPlayerResources.class);
	private static final char DOWN_ARROW_CHAR = '\u25BE';
	private static final PlaybackQuality PLAYBACK_QUALITY = PlaybackQuality.small;
	private static final int MILLISECONDS = 1000;
	private static final int REFRESH_TIME = 250;
	private static final boolean IS_PROGRESS_TEXT_VISIBLE = false;
	private static final int FULL_VOLUME = 1;
	private static final int MUTE_VOLUME = 0;
	private static final int INIT_VOLUME = FULL_VOLUME;
	private static final boolean IS_AUTOPLAY = false;
	private static final String PLAYER_HEIGHT = "115px";
	private static final String PLAYER_WIDTH = "147px";
	private DockPanel mainPanel = new DockPanel();
	private TabPanel tabsPanel = new DecoratedTabPanel();
	private VerticalPanel playerPanel = new VerticalPanel();
	private HorizontalPanel controlsPanel = new HorizontalPanel();
	private VerticalPanel formPanel = new VerticalPanel();
	private HorizontalPanel headerPanel = new HorizontalPanel();
	private VerticalPanel searchbarPanel = new VerticalPanel();
	private HorizontalPanel searchControlsPanel = new HorizontalPanel();
	private FlexTable searchResultsTable = new FlexTable();

	private String songTitleDisplay = "";
	private HTML songDetailsHTML = new HTML();
	private boolean isPlaying = false;
	private boolean isMinimized = false;
	private ChromelessPlayer player;

	private final YoutubeDataServiceAsync youtubeDataService = GWT
			.create(YoutubeDataService.class);

	private final DataStoreServiceAsync dataStoreService = GWT
			.create(DataStoreService.class);

	private static final boolean IS_ANIMATION_ENABLED = true;
	private static final int PLAYLISTS_TAB = 0;
	private static final int SEARCH_TAB = 1;
	private static final Label NOW_PLAYING_LABEL = new Label("Now Playing");
	private Button playPauseButton;
	private ProgressBar progressBar = new ProgressBar();
	private Timer playPositionTimer;

	private Map<String, Playlist> playlists = new TreeMap<String, Playlist>();
	private ListBox playlistsBox = new ListBox();
	private VerticalPanel searchPanelWrapper = new VerticalPanel();
	private VerticalPanel playlistPanelWrapper = new VerticalPanel();
	private VerticalPanel searchVerticalPanel;
	private ScrollPanel searchResultsScrollPanel;
	private int jsonRequestId = 0;
	private MultiWordSuggestOracle oracle;
	private FlexTable playlistTable;
	private VerticalPanel playlistVerticalPanel;
	private ScrollPanel playlistsScrollPanel;
	private CheckBox shuffleBox;

	private Playlist previousPlaylist = null;
	private Playlist currentPlaylist = null;
	private CheckBox repeatBox;

	private PeopleService people;
	private Person viewer;

	private VerticalPanel communityWrapperPanel;

	private VerticalPanel communityVerticalPanel;

	private IPlayerPreferences prefs;
	private SetPrefsFeature setPrefsFeature;

	/**
	 * This is the entry point method.
	 */
	@Override
	protected void init(IPlayerPreferences preferences) {
		prefs = preferences;
		resources.css().ensureInjected();
		GadgetsGwtRpc.redirectThroughProxy((ServiceDefTarget) dataStoreService);
		GadgetsGwtRpc
				.redirectThroughProxy((ServiceDefTarget) youtubeDataService);
		people.newGetViewerRequestBuilder().setFields(Person.THUMBNAIL_URL,
				Person.ID, Person.DISPLAY_NAME).build().execute(
				new Callback<Person>() {

					@Override
					public void onSuccess(Person object) {
						viewer = object;
						loadIPlayer();
					}

					@Override
					public void onFail(OsapiError error) {
						// TODO Auto-generated method stub
						Window.alert(error.getMessage());
					}
				});
		Timer heightTimer = new Timer() {

			@Override
			public void run() {
				dynamicHeightFeature.adjustHeight();
			}
		};
		heightTimer.scheduleRepeating(1000);
	}

	protected void loadIPlayer() {
		Playlist playlist = null;
		String playlistName = prefs.playlistName().getValue();
		if (playlistName != null && !playlistName.isEmpty()) {
			playlist = new Playlist(playlistName);
		}
		loadPlaylists(playlist);
		playPositionTimer = new Timer() {

			@Override
			public void run() {
				displaySongDetails();
				double duration = player.getMediaDuration();
				double playPosition = player.getPlayPosition();
				double percent = playPosition / duration;
				double curProgress = progressBar.getMaxProgress() * percent;
				progressBar.setProgress(curProgress);
			}
		};
		createYouTubePlayer();
		setPanels();
		createControls();
		createForm();
		addStyles();
		RootPanel.get().add(mainPanel);
	}

	/**
	 * get the index of the playlist within the list box
	 * 
	 * @param listBox
	 * @param playlist
	 * @return index of the playlist within the list box or -1 if it is not
	 *         found
	 */
	public int getPlaylistListBoxIndex(ListBox listBox, Playlist playlist) {
		if (playlist != null) {
			for (int i = 0; i < listBox.getItemCount(); i++) {
				if (playlist.getName().equalsIgnoreCase(listBox.getValue(i))) {
					return i;
				}
			}
		}
		return -1;
	}

	private void createPlaylistPanel() {
		final DisclosurePanel playlistPanel = new DisclosurePanel();
		playlistPanel.setStyleName("tabsDisclosurePanel");
		createPlaylistsBox();
		final IPlayer iPlayer = this;
		playlistsBox.addChangeHandler(new ChangeHandler() {

			@Override
			public void onChange(ChangeEvent event) {
				String playlistName = playlistsBox.getItemText(playlistsBox
						.getSelectedIndex());
				if (playlistName.equalsIgnoreCase("Select Playlist")
						&& getCurrentPlaylist() != null) {
					int i = getPlaylistListBoxIndex(playlistsBox,
							getCurrentPlaylist());
					playlistsBox.setItemSelected(i, true);
					return;
				}
				if (playlistName.equalsIgnoreCase("New...")) {
					Playlist currentPlaylist = getCurrentPlaylist();
					if (currentPlaylist != null) {
						int i = getPlaylistListBoxIndex(playlistsBox,
								currentPlaylist);
						i = (i == -1) ? 0 : i;
						playlistsBox.setItemSelected(i, true);
					} else {
						playlistsBox.setItemSelected(0, true);
					}
					AddPlaylistDialog addPlaylistDialog = new AddPlaylistDialog(
							iPlayer);
					addPlaylistDialog.setPopupPosition(80, 220);
					addPlaylistDialog.show();
				} else {
					setWidgetInPanel(playlistVerticalPanel, new Image(resources
							.loadingAnimation()));
					currentPlaylist = loadPlaylist(playlistName);
				}
			}
		});

		HorizontalPanel headerPanel = new HorizontalPanel();
		final ToggleButton button = new ToggleButton(new Image(resources
				.minimizePanelButton()), new Image(resources
				.maximizePanelButton()));
		button.setStyleName("panelSizeButton");
		button.addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				handlePanelSizeClicked(button, playlistPanel);
			}
		});
		MenuBar menuBar = new MenuBar(false);
		MenuBar optionsMenuBar = new MenuBar(true);
		optionsMenuBar.addItem("Rename", new Command() {

			@Override
			public void execute() {
				RenamePlaylistDialog dialog = new RenamePlaylistDialog(iPlayer);
				dialog.setPopupPosition(80, 220);
				dialog.show();
			}
		});
		optionsMenuBar.addItem("Delete", new Command() {

			@Override
			public void execute() {
				DeletePlaylistDialog dialog = new DeletePlaylistDialog(iPlayer);
				dialog.setPopupPosition(75, 220);
				dialog.show();
			}
		});
		optionsMenuBar.addItem("Help", new Command() {

			@Override
			public void execute() {
				HelpDialog dialog = new HelpDialog();
				dialog.center();
			}
		});
		menuBar.addItem("Options" + DOWN_ARROW_CHAR, optionsMenuBar);
		menuBar.addStyleName("playlistMenu");
		menuBar.setAnimationEnabled(IS_ANIMATION_ENABLED);
		headerPanel.add(playlistsBox);
		headerPanel.add(menuBar);
		headerPanel.add(button);
		headerPanel.setWidth("100%");
		headerPanel.setCellHorizontalAlignment(playlistsBox,
				HasHorizontalAlignment.ALIGN_LEFT);
		headerPanel.setCellHorizontalAlignment(button,
				HasHorizontalAlignment.ALIGN_RIGHT);
		headerPanel.setCellHorizontalAlignment(menuBar,
				HasHorizontalAlignment.ALIGN_RIGHT);

		playlistTable = new FlexTable();
		playlistTable.setWidth("100%");
		playlistsScrollPanel = new ScrollPanel(playlistTable);
		playlistsScrollPanel.setStyleName("playlistScrollPanel");

		playlistVerticalPanel = new VerticalPanel();
		playlistVerticalPanel
				.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
		playlistVerticalPanel.setStyleName("playlistScrollPanel");
		playlistVerticalPanel.add(playlistsScrollPanel);

		playlistPanel.add(playlistVerticalPanel);
		playlistPanel.setOpen(true);
		playlistPanel.setAnimationEnabled(IS_ANIMATION_ENABLED);

		playlistPanelWrapper.add(headerPanel);
		playlistPanelWrapper.add(playlistPanel);
	}

	/**
	 * 
	 * @param playlist
	 *            the playlist to select after load
	 */
	public void loadPlaylists(final Playlist playlist) {
		dataStoreService.getPlaylists(viewer.getId(),
				new AsyncCallback<Map<String, Playlist>>() {

					@Override
					public void onSuccess(Map<String, Playlist> result) {
						for (Entry<String, Playlist> entry : result.entrySet()) {
							String key = entry.getKey();
							if (playlists.containsKey(key)) {
								result.put(key, playlists.get(key));
							}
						}
						playlists = result;

						createPlaylistsBox();
						selectPlaylist(playlist);
					}

					@Override
					public void onFailure(Throwable caught) {
						// TODO Auto-generated method stub

					}
				});
	}

	private void selectPlaylist(Playlist playlist) {
		NativeEvent event = Document.get().createChangeEvent();
		int i = getPlaylistListBoxIndex(playlistsBox, playlist);
		if (i != -1) {
			playlistsBox.setItemSelected(i, true);
			ChangeEvent.fireNativeEvent(event, playlistsBox);
		} else {
			playlistsBox.setItemSelected(0, true);
			createPlaylistTable(playlistTable, null);
		}
	}

	private void createPlaylistsBox() {
		playlistsBox.setStyleName("header");
		playlistsBox.clear();
		playlistsBox.addItem("Select Playlist");
		for (Playlist playlist : playlists.values()) {
			playlistsBox.addItem(playlist.getName());
		}
		playlistsBox.addItem("New...");
		playlistsBox.setSelectedIndex(0);
	}

	/**
	 * set the application layout
	 */
	private void setPanels() {
		createPlaylistPanel();
		tabsPanel.add(playlistPanelWrapper, "Playlists");
		createSearchPanel();
		tabsPanel.add(searchPanelWrapper, "Search");
		createCommunityPanel();
		tabsPanel.add(communityWrapperPanel, "Community");
		tabsPanel.setAnimationEnabled(IS_ANIMATION_ENABLED);
		tabsPanel.selectTab(PLAYLISTS_TAB);

		AbsolutePanel bottomPanel = new AbsolutePanel();
		createBottomPanel(bottomPanel);

		mainPanel.add(bottomPanel, DockPanel.SOUTH);
		playerPanel.add(player);
		progressBar.setTextVisible(IS_PROGRESS_TEXT_VISIBLE);
		final FocusPanel progressBarPanel = new FocusPanel(progressBar);
		createProgressBarPanel(progressBarPanel);

		playerPanel.add(progressBarPanel);
		playerPanel.add(controlsPanel);
		mainPanel.add(playerPanel, DockPanel.EAST);

		mainPanel.add(formPanel, DockPanel.WEST);
	}

	private void createCommunityPanel() {
		final ScrollPanel scrollPanel = new ScrollPanel();
		scrollPanel.setStyleName("communityScrollPanel");
		final DisclosurePanel communityPanel = new DisclosurePanel();
		communityPanel.setStyleName("tabsDisclosurePanel");
		communityPanel.setOpen(true);
		communityPanel.setAnimationEnabled(IS_ANIMATION_ENABLED);
		communityWrapperPanel = new VerticalPanel();

		final HorizontalPanel header = new HorizontalPanel();
		// create list box
		final ListBox top20ListBox = new ListBox();
		top20ListBox.addItem("World");
		top20ListBox.addItem("Friends");
		top20ListBox.addChangeHandler(new ChangeHandler() {

			@Override
			public void onChange(ChangeEvent event) {
				String type = top20ListBox.getValue(top20ListBox
						.getSelectedIndex());
				displayTopTracks(scrollPanel, type);
			}
		});
		top20ListBox.setStyleName("header");
		header.add(top20ListBox);
		header.setCellWidth(top20ListBox, "70%");
		// create buttons
		String inboxButtonText = "Inbox";
		Button inboxButton = new Button(inboxButtonText, new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				handleInboxClicked(header, scrollPanel);
			}
		});
		inboxButton.setStyleName("button");
		Button top20Button = new Button("Top 20", new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				handleTop20Clicked(header, scrollPanel);
			}
		});
		top20Button.setStyleName("button");
		header.add(inboxButton);
		header.add(top20Button);
		// create minimize button
		final ToggleButton button = new ToggleButton(new Image(resources
				.minimizePanelButton()), new Image(resources
				.maximizePanelButton()));
		button.setStyleName("panelSizeButton");
		button.addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				handlePanelSizeClicked(button, communityPanel);
			}
		});
		header.add(button);

		header.setWidth("100%");
		header.setCellHorizontalAlignment(top20ListBox,
				HasHorizontalAlignment.ALIGN_LEFT);
		header.setCellHorizontalAlignment(inboxButton,
				HasHorizontalAlignment.ALIGN_RIGHT);
		header.setCellHorizontalAlignment(top20Button,
				HasHorizontalAlignment.ALIGN_RIGHT);
		header.setCellHorizontalAlignment(button,
				HasHorizontalAlignment.ALIGN_RIGHT);

		communityVerticalPanel = new VerticalPanel();
		communityVerticalPanel.add(scrollPanel);
		communityVerticalPanel
				.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
		communityVerticalPanel.setStyleName("communityScrollPanel");

		displayTopTracks(scrollPanel, top20ListBox.getValue(top20ListBox
				.getSelectedIndex()));

		communityPanel.add(communityVerticalPanel);

		communityWrapperPanel.add(header);
		communityWrapperPanel.add(communityPanel);
	}

	private void handleTop20Clicked(HorizontalPanel header,
			final ScrollPanel panel) {
		header.remove(0);
		// create list box
		final ListBox top20ListBox = new ListBox();
		top20ListBox.addItem("World");
		top20ListBox.addItem("Friends");
		top20ListBox.addChangeHandler(new ChangeHandler() {

			@Override
			public void onChange(ChangeEvent event) {
				String type = top20ListBox.getValue(top20ListBox
						.getSelectedIndex());
				displayTopTracks(panel, type);
			}
		});
		top20ListBox.setStyleName("header");
		header.insert(top20ListBox, 0);
		header.setCellWidth(top20ListBox, "70%");

		displayTopTracks(panel, top20ListBox.getValue(top20ListBox
				.getSelectedIndex()));
	}

	private void handleInboxClicked(HorizontalPanel header,
			final ScrollPanel panel) {
		setWidgetInPanel(communityVerticalPanel, new Image(resources
				.loadingAnimation()));
		header.remove(0);
		Label inboxLabel = new Label("Inbox");
		inboxLabel.setStyleName("header");
		header.insert(inboxLabel, 0);
		header.setCellWidth(inboxLabel, "70%");

		dataStoreService.getTrackTransactions(viewer.getId(),
				new AsyncCallback<List<TrackTransaction>>() {

					@Override
					public void onSuccess(List<TrackTransaction> result) {
						final FlexTable table = new FlexTable();
						table.setCellPadding(0);
						table.setCellSpacing(0);
						for (final TrackTransaction transaction : result) {
							Track track = new Track(transaction.getTitle(),
									transaction.getArtist(), transaction
											.getDuration(), transaction
											.getVideoID(), transaction
											.getThumbnailURL(), 0);
							final Label fromLabel = new Label();
							fromLabel.setStyleName("standardLabel");
							fromLabel.addClickHandler(new ClickHandler() {

								@Override
								public void onClick(ClickEvent event) {
									handleTrackTransactionClicked(transaction);
								}
							});
							Label trackDisplayStrLabel = new Label(track
									.toString());
							trackDisplayStrLabel.setStyleName("standardLabel");
							trackDisplayStrLabel
									.addClickHandler(new ClickHandler() {

										@Override
										public void onClick(ClickEvent event) {
											handleTrackTransactionClicked(transaction);
										}
									});
							Label dateLabel = new Label(IPlayerUtils
									.formatDate(transaction.getDate()));
							dateLabel.setStyleName("standardLabel");
							dateLabel
									.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT);

							dateLabel.addClickHandler(new ClickHandler() {

								@Override
								public void onClick(ClickEvent event) {
									handleTrackTransactionClicked(transaction);
								}
							});
							final int row = table.getRowCount();
							table.getRowFormatter().setStyleName(row,
									"flexTableRow");
							table.setWidget(row, 0, fromLabel);
							table.getCellFormatter().setStyleName(row, 0,
									"flexTableRow");
							table.setWidget(row, 1, trackDisplayStrLabel);
							table.getCellFormatter().setStyleName(row, 1,
									"flexTableRow");
							table.setWidget(row, 2, dateLabel);
							table.getCellFormatter().setStyleName(row, 2,
									"flexTableRow");
							table.getCellFormatter().setHorizontalAlignment(
									row, 2, HasHorizontalAlignment.ALIGN_RIGHT);
							Button deleteButton = new Button();
							deleteButton.setStyleName("deleteButton");
							deleteButton.addClickHandler(new ClickHandler() {

								@Override
								public void onClick(ClickEvent event) {
									deleteTrackTransaction(transaction, table,
											row);
								}
							});
							table.setWidget(row, 3, deleteButton);
							table.getCellFormatter().setAlignment(row, 3,
									HasHorizontalAlignment.ALIGN_RIGHT,
									HasVerticalAlignment.ALIGN_MIDDLE);
							table.getCellFormatter().setStyleName(row, 3,
									"flexTableRow");

							people.newGetPersonByIdRequestBuilder(
									transaction.getSenderID()).setFields(
									Person.THUMBNAIL_URL, Person.ID,
									Person.DISPLAY_NAME).build().execute(
									new Callback<Person>() {

										@Override
										public void onSuccess(Person object) {
											fromLabel.setText(object
													.getDisplayName());
										}

										@Override
										public void onFail(OsapiError error) {
											// TODO Auto-generated method stub

										}
									});
						}
						panel.setWidget(table);
						setWidgetInPanel(communityVerticalPanel, panel);
					}

					@Override
					public void onFailure(Throwable caught) {
						// TODO Auto-generated method stub

					}
				});
	}

	private void handleTrackTransactionClicked(
			final TrackTransaction transaction) {
		people
				.newGetPersonByIdRequestBuilder(transaction.getSenderID())
				.setFields(Person.THUMBNAIL_URL, Person.ID, Person.DISPLAY_NAME)
				.build().execute(new Callback<Person>() {

					@Override
					public void onSuccess(Person object) {
						IncomingTrackDialog dialog = new IncomingTrackDialog(
								transaction, IPlayer.this, object
										.getDisplayName());
						dialog.center();
					}

					@Override
					public void onFail(OsapiError error) {
						// TODO Auto-generated method stub

					}
				});
	}

	private void deleteTrackTransaction(TrackTransaction transaction,
			final FlexTable table, final int row) {
		dataStoreService.deleteTrackTransaction(viewer.getId(), transaction,
				new AsyncCallback<Void>() {

					@Override
					public void onSuccess(Void result) {
						table.removeRow(row);
					}

					@Override
					public void onFailure(Throwable caught) {
						// TODO Auto-generated method stub

					}
				});
	}

	/**
	 * creates the panel that encapsulates the progress bar
	 * 
	 * @param progressBarPanel
	 *            the panel to create
	 */
	private void createProgressBarPanel(final FocusPanel progressBarPanel) {
		progressBarPanel.addMouseUpHandler(new MouseUpHandler() {

			@Override
			public void onMouseUp(MouseUpEvent event) {
				long mediaDuration = player.getMediaDuration();
				if (mediaDuration > 0) {
					double width = progressBar.getOffsetWidth();
					double percent = event.getX() / width;
					double curProgress = progressBar.getMaxProgress() * percent;
					double position = mediaDuration * percent;
					position /= MILLISECONDS;
					player.setPlayPosition(position);
					progressBar.setProgress(curProgress);
				}
			}
		});
		progressBarPanel.addMouseDownHandler(new MouseDownHandler() {

			@Override
			public void onMouseDown(MouseDownEvent event) {
				progressBarPanel.setFocus(false);
			}
		});
	}

	/**
	 * creates the bottom panel of the application
	 * 
	 * @param bottomPanel
	 *            the panel to create
	 */
	private void createBottomPanel(AbsolutePanel bottomPanel) {
		bottomPanel.add(tabsPanel);
		shuffleBox = new CheckBox("Shuffle");
		shuffleBox.addValueChangeHandler(new ValueChangeHandler<Boolean>() {

			@Override
			public void onValueChange(ValueChangeEvent<Boolean> event) {
				setPrefsFeature.set(prefs.shuffle(), event.getValue());
			}
		});
		if (prefs.shuffle() != null) {
			shuffleBox.setValue(prefs.shuffle().getValue());
		}
		repeatBox = new CheckBox("Repeat");
		repeatBox.addValueChangeHandler(new ValueChangeHandler<Boolean>() {

			@Override
			public void onValueChange(ValueChangeEvent<Boolean> event) {
				setPrefsFeature.set(prefs.repeat(), event.getValue());
			}
		});
		if (prefs.repeat() != null) {
			repeatBox.setValue(prefs.repeat().getValue());
		}
		bottomPanel.add(shuffleBox, 215, 5);
		bottomPanel.add(repeatBox, 275, 5);
	}

	/**
	 * creates and sets-up the Chromeless Player
	 */
	private void createYouTubePlayer() {
		try {
			PlayerParameters params = new PlayerParameters();
			params.setAutoplay(IS_AUTOPLAY);
			player = new ChromelessPlayer("", params, PLAYER_WIDTH,
					PLAYER_HEIGHT);
			player.setVolume(INIT_VOLUME);

			player.setConfigParameter(ConfigParameter.TransparencyMode,
					TransparencyMode.OPAQUE);
			player.setPlaybackQuality(PLAYBACK_QUALITY);

			player.addPlayStateHandler(new PlayStateHandler() {

				@Override
				public void onPlayStateChanged(PlayStateEvent event) {
					if (event.getPlayState().equals(
							PlayStateEvent.State.Started)) {
						isPlaying = true;
						playPauseButton.setStyleName("pauseButton");
						playPositionTimer.scheduleRepeating(REFRESH_TIME);
					}
					if (event.getPlayState()
							.equals(PlayStateEvent.State.Paused)
							|| event.getPlayState().equals(
									PlayStateEvent.State.Finished)) {
						isPlaying = false;
						playPauseButton.setStyleName("playButton");
						playPositionTimer.cancel();
					}
					if (event.getPlayState().equals(
							PlayStateEvent.State.Finished)) {
						playNextTrack();
					}
				}
			});

		} catch (PluginNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (PluginVersionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private void playNextTrack() {
		Track nextTrack = getCurrentPlaylist().getNextTrack(isShuffle(),
				isRepeat());
		if (nextTrack != null) {
			loadTrack(nextTrack, false);
		}
	}

	/**
	 * @return
	 */
	private Boolean isRepeat() {
		return repeatBox.getValue();
	}

	/**
	 * @return
	 */
	private Boolean isShuffle() {
		return shuffleBox.getValue();
	}

	private void playPreviousTrack() {
		Track prevTrack = getCurrentPlaylist().getPreviousTrack();
		if (prevTrack != null) {
			loadTrack(prevTrack, false);
		}
	}

	/**
	 * creates the search panel
	 */
	private void createSearchPanel() {
		searchResultsScrollPanel = new ScrollPanel(searchResultsTable);
		searchResultsScrollPanel.setStyleName("searchResultsScrollPanel");
		searchVerticalPanel = new VerticalPanel();
		searchVerticalPanel
				.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
		searchVerticalPanel.setStyleName("searchResultsScrollPanel");
		searchVerticalPanel.add(searchResultsScrollPanel);
		final DisclosurePanel searchPanel = new DisclosurePanel();
		searchPanel.setStyleName("tabsDisclosurePanel");
		HorizontalPanel headerPanel = new HorizontalPanel();
		final ToggleButton button = new ToggleButton(new Image(resources
				.minimizePanelButton()), new Image(resources
				.maximizePanelButton()));
		button.setStyleName("panelSizeButton");
		button.addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				handlePanelSizeClicked(button, searchPanel);
			}
		});
		Label searchResultsLabel = new Label("Search Results");
		searchResultsLabel.setStyleName("header");
		headerPanel.add(searchResultsLabel);
		headerPanel.add(button);
		headerPanel.setWidth("100%");
		headerPanel.setCellHorizontalAlignment(searchResultsLabel,
				HasHorizontalAlignment.ALIGN_LEFT);
		headerPanel.setCellHorizontalAlignment(button,
				HasHorizontalAlignment.ALIGN_RIGHT);
		searchPanel.add(searchVerticalPanel);
		searchPanel.setOpen(true);
		searchPanel.setAnimationEnabled(IS_ANIMATION_ENABLED);
		searchPanelWrapper.add(headerPanel);
		searchPanelWrapper.add(searchPanel);
	}

	private void setWidgetInPanel(CellPanel panel, Widget widget) {
		panel.remove(0);
		panel.add(widget);
		if (widget instanceof Image) {
			panel.setCellHorizontalAlignment(widget,
					HasHorizontalAlignment.ALIGN_CENTER);
		}
	}

	protected void handlePanelSizeClicked(ToggleButton button,
			DisclosurePanel panel) {
		button.setFocus(false);
		boolean isMinimize = !button.isDown();
		panel.setOpen(isMinimize);
	}

	/**
	 * creates the player control buttons
	 */
	private void createControls() {
		Button previousButton = new Button();
		previousButton.addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				playPreviousTrack();
			}
		});
		previousButton.setStyleName("previousButton");
		playPauseButton = new Button();
		playPauseButton.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				handlePlayPauseButtonClick();
			}
		});
		playPauseButton.setStyleName("playButton");
		Button nextButton = new Button();
		nextButton.addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				playNextTrack();
			}
		});
		nextButton.setStyleName("nextButton");
		final Button volumeButton = new Button();
		volumeButton.setStyleName("volumeButton");
		volumeButton.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				handleVolumeButtonClick(volumeButton);
			}
		});
		final Button sizeButton = new Button();
		sizeButton.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				handleSizeButtonClick(sizeButton);
			}
		});
		sizeButton.setStyleName("minimizeButton");

		controlsPanel.add(previousButton);
		controlsPanel.add(playPauseButton);
		controlsPanel.add(nextButton);
		controlsPanel.add(volumeButton);
		controlsPanel.add(sizeButton);
	}

	/**
	 * handles the event of the video size button click
	 */
	private void handleSizeButtonClick(Button button) {
		isMinimized = !isMinimized;
		if (isMinimized) {
			button.setStyleName("minimizeButton");
			// player.setVisible(false);
			// formPanel.remove(NOW_PLAYING_LABEL);
			// formPanel.remove(songDetailsHTML);
			// playerPanel.insert(songDetailsHTML,1);
			// playerPanel.insert(NOW_PLAYING_LABEL,1);
		} else {
			button.setStyleName("maximizeButton");
			// player.setVisible(true);
			// playerPanel.remove(NOW_PLAYING_LABEL);
			// playerPanel.remove(songDetailsHTML);
			// formPanel.add(NOW_PLAYING_LABEL);
			// formPanel.add(songDetailsHTML);
		}
	}

	/**
	 * handles the event of the volume button click
	 */
	private void handleVolumeButtonClick(Button button) {
		double volume = player.getVolume();
		if (volume == FULL_VOLUME) {
			button.setStyleName("muteButton");
			player.setVolume(MUTE_VOLUME);
		} else {
			button.setStyleName("volumeButton");
			player.setVolume(FULL_VOLUME);
		}
	}

	/**
	 * handles the event of the search button click
	 * 
	 * @param title
	 *            the song title to search for
	 * @param changeTab
	 *            TODO
	 */
	private void handleSearch(String title, final boolean changeTab) {
		if (title != null) {
			setWidgetInPanel(searchVerticalPanel, new Image(resources
					.loadingAnimation()));
			searchResultsTable.clear(true);
			searchResultsTable.removeAllRows();
			if (changeTab) {
				tabsPanel.selectTab(SEARCH_TAB);
			}
			youtubeDataService.searchVideo(title,
					new AsyncCallback<List<Track>>() {
						@Override
						public void onFailure(Throwable caught) {
							// TODO Auto-generated method stub

						}

						@Override
						public void onSuccess(List<Track> result) {
							displaySearchResults(result);
							if (!changeTab && (getCurrentPlaylist() != null)
									&& (result != null) && !result.isEmpty()) {
								addTrack(result.get(0), getCurrentPlaylist());
							}
						}
					});
			getSuggestions(title);
		}
	}

	/**
	 * displays the search results
	 * 
	 * @param results
	 *            the search results
	 */
	private void displaySearchResults(List<Track> results) {
		int row = 0;
		for (final Track result : results) {
			TrackDisplay trackDisplay = new ThumbnailTrackDisplay(result);
			trackDisplay.setWidth("100%");
			searchResultsTable.setWidget(row, 0, trackDisplay);
			searchResultsTable.getCellFormatter().setStyleName(row, 0,
					"flexTableRow");
			row++;
			trackDisplay.addClickHandler(new ClickHandler() {

				@Override
				public void onClick(ClickEvent event) {
					AddTrackDialog addTrackDialog = new AddTrackDialog(
							IPlayer.this);
					addTrackDialog.setTrack(result);
					addTrackDialog.setPopupPosition(20, 150);
					addTrackDialog.show();
				}
			});
		}
		setWidgetInPanel(searchVerticalPanel, searchResultsScrollPanel);
	}

	/**
	 * handles the event of the play/pause button click
	 */
	private void handlePlayPauseButtonClick() {
		if (isPlaying) {
			player.pauseMedia();
		} else {
			try {
				player.playMedia();
			} catch (PlayException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	/**
	 * creates the players logo, search-bar, and details area
	 */
	private void createForm() {
		Image logo = new Image(resources.logo());
		headerPanel.add(logo);
		headerPanel.add(searchbarPanel);
		oracle = new MultiWordSuggestOracle();
		final SuggestBox searchBox = new SuggestBox(oracle);
		searchBox.addKeyPressHandler(new KeyPressHandler() {

			@Override
			public void onKeyPress(KeyPressEvent event) {
				char pressedChar = event.getCharCode();
				if (pressedChar == KeyCodes.KEY_ENTER) {
					if (!searchBox.isSuggestionListShowing()) {
						String title = searchBox.getText();
						handleSearch(title, true);
					}
				}
			}
		});
		searchBox.addKeyUpHandler(new KeyUpHandler() {

			@Override
			public void onKeyUp(KeyUpEvent event) {
				getSuggestions(searchBox.getValue());
			}
		});
		searchBox.setAnimationEnabled(false);
		searchBox.setAutoSelectEnabled(false);
		searchBox.setStyleName("searchBox");
		searchbarPanel.add(searchBox);
		searchbarPanel.add(searchControlsPanel);
		Button searchButton = new Button("Search");
		searchButton.setStyleName("button");
		searchButton.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				String title = searchBox.getText();
				handleSearch(title, true);
			}
		});
		Button addSongButton = new Button("Add Song");
		addSongButton.setStyleName("button");
		addSongButton.addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				String title = searchBox.getText();
				handleSearch(title, false);
			}
		});
		searchControlsPanel.add(searchButton);
		searchControlsPanel.add(addSongButton);

		formPanel.add(headerPanel);
		formPanel.add(NOW_PLAYING_LABEL);
		formPanel.add(songDetailsHTML);

	}

	/**
	 * adds styles to the application elements
	 */
	private void addStyles() {
		songDetailsHTML.addStyleName("songDetailsHTML");
		NOW_PLAYING_LABEL.addStyleName("nowPlayingLabel");
		mainPanel.addStyleName("mainPanel");
		searchbarPanel.setCellHorizontalAlignment(searchControlsPanel,
				HasHorizontalAlignment.ALIGN_CENTER);
		playerPanel.setCellHorizontalAlignment(controlsPanel,
				HasHorizontalAlignment.ALIGN_CENTER);
		mainPanel.setSpacing(2);
		mainPanel.setStyleName("mainPanel");
		playerPanel.setSpacing(0);
		playerPanel.setStyleName("playerPanel");
		formPanel.setSpacing(5);
		searchControlsPanel.setSpacing(3);
		controlsPanel.setSpacing(1);
		headerPanel.setSpacing(2);
		searchResultsTable.setWidth("100%");
		tabsPanel.addStyleName("tabsPanel");
	}

	/**
	 * @return the player
	 */
	public ChromelessPlayer getPlayer() {
		return player;
	}

	/**
	 * loads and plays the given track
	 * 
	 * @param track
	 *            the track to play
	 * @param isPreview
	 *            TODO
	 */
	public void loadTrack(Track track, boolean isPreview) {
		try {
			Playlist currentPlaylist = getCurrentPlaylist();
			if (currentPlaylist != null
					&& currentPlaylist.isFirstPlayedTrack(track)
					&& previousPlaylist != null) {
				previousPlaylist.reset();
			}
			if (!isPreview) {
				previousPlaylist = getCurrentPlaylist();
			}
			handleTrackHighlights();
			songTitleDisplay = track.toString();
			player.loadMedia(IPlayerUtils.getYouTubeURL(track.getVideoID()));
			displaySongDetails();
		} catch (LoadException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * handles the played track highlight
	 */
	private void handleTrackHighlights() {
		Playlist playlist = getCurrentPlaylist();
		if (playlist != null) {
			Integer prevTrackIndex = playlist.getPreviousTrackIndex();
			if (prevTrackIndex != -1) {
				playlistTable.getRowFormatter().setStyleName(prevTrackIndex,
						"flexTableRow");
				// int cellCount = playlistTable.getCellCount(prevTrackIndex);
				// for (int i = 0; i < cellCount; i++) {
				// playlistTable.getCellFormatter().setStyleName(
				// prevTrackIndex, i, "flexTableRow");
				// }
			}
			Integer currTrackIndex = playlist.getCurrentTrackIndex();
			if (currTrackIndex != -1) {
				playlistTable.getRowFormatter().setStyleName(currTrackIndex,
						"flexTablePlayRow");
				// int cellCount = playlistTable.getCellCount(currTrackIndex);
				// for (int i = 0; i < cellCount; i++) {
				// playlistTable.getCellFormatter().setStyleName(
				// currTrackIndex, i, "flexTablePlayRow");
				// }
			}
		}
	}

	/**
	 * displays the songs details
	 */
	private void displaySongDetails() {
		String displayStr = songTitleDisplay + "<br>";

		displayStr += IPlayerUtils.formatTime((int) Math.ceil(player
				.getPlayPosition()
				/ MILLISECONDS))
				+ " / "
				+ IPlayerUtils.formatTime((int) Math.ceil(player
						.getMediaDuration()
						/ MILLISECONDS));
		songDetailsHTML.setHTML(displayStr);
	}

	/**
	 * adds the track to the given playlist
	 * 
	 * @param track
	 *            the track to add
	 * @param playlist
	 *            the playlist to store the track into
	 */
	public void addTrack(final Track track, final Playlist playlist) {
		dataStoreService.addTrack(viewer.getId(), track, playlist,
				new AsyncCallback<Void>() {

					@Override
					public void onSuccess(Void result) {
						Playlist currPlaylist = getCurrentPlaylist();
						if (currPlaylist.equals(playlist)) {
							currPlaylist.loadTracks(IPlayer.this);
						}
					}

					@Override
					public void onFailure(Throwable caught) {
						// TODO Auto-generated method stub
						Window.alert(caught.getMessage());
					}
				});
	}

	/**
	 * add a playlist
	 * 
	 * @param playlist
	 *            the playlist to add
	 */
	public void addPlaylist(final Playlist playlist) {
		dataStoreService.addPlaylist(viewer.getId(), playlist,
				new AsyncCallback<Void>() {

					@Override
					public void onSuccess(Void result) {
						loadPlaylists(playlist);
					}

					@Override
					public void onFailure(Throwable caught) {
						// TODO Auto-generated method stub
						Window.alert(caught.getMessage());
					}
				});
	}

	/**
	 * @param playlists
	 *            the playlists to set
	 */
	public void setPlaylists(Map<String, Playlist> playlists) {
		this.playlists = playlists;
	}

	/**
	 * @return the playlists
	 */
	public Collection<Playlist> getPlaylists() {
		return playlists.values();
	}

	/**
	 * sends a request to suggestion api for auto-completion
	 * 
	 * @param query
	 *            the query to auto-complete
	 */
	private void getSuggestions(String query) {
		String url = "http://suggestqueries.google.com/complete/search?hl=en&ds=yt&json=t&q="
				+ query;

		// Append the name of the callback function to the JSON URL.
		url = URL.encode(url) + "&jsonp=";

		// Send request to server by replacing RequestBuilder code with a call
		// to a JSNI method.
		getJson(jsonRequestId++, url, this);

	}

	/**
	 * Make call to remote server.
	 */
	public native static void getJson(int requestId, String url, IPlayer handler) /*-{
		var callback = "callback" + requestId;

		// [1] Create a script element.
		var script = document.createElement("script");
		script.setAttribute("src", url+callback);
		script.setAttribute("type", "text/javascript");

		// [2] Define the callback function on the window object.
		window[callback] = function(jsonObj) {

		handler.@com.tau.iplayer.client.IPlayer::handleJsonResponse(Lcom/google/gwt/core/client/JavaScriptObject;)(jsonObj);
		window[callback + "done"] = true;
		}

		// [4] JSON download has 1-second timeout.
		setTimeout(function() {
		if (!window[callback + "done"]) {
		handler.@com.tau.iplayer.client.IPlayer::handleJsonResponse(Lcom/google/gwt/core/client/JavaScriptObject;)(null);
		}

		// [5] Cleanup. Remove script and callback elements.
		document.body.removeChild(script);
		delete window[callback];
		delete window[callback + "done"];
		}, 1000);

		// [6] Attach the script element to the document body.
		document.body.appendChild(script);
	}-*/;

	/**
	 * Handle the response to the request for stock data from a remote server.
	 */
	public void handleJsonResponse(JavaScriptObject jso) {
		List<String> results = new ArrayList<String>();
		if (jso == null) {
			System.out.println("Couldn't retrieve JSON");
			return;
		}

		JSONObject json = new JSONObject(jso);
		JSONValue jsonValue = json.get("1");
		JSONArray jsonArray = jsonValue.isArray();

		for (int i = 0; i < jsonArray.size(); i++) {
			String result = jsonArray.get(i).isString().stringValue();
			results.add(result);
		}
		oracle.clear();
		oracle.addAll(results);
	}

	/**
	 * loads a playlist with the given name
	 * 
	 * @param playlistName
	 *            the name of the playlist
	 * @return the loaded playlist
	 */
	private Playlist loadPlaylist(String playlistName) {
		Playlist playlist = this.playlists.get(playlistName);
		if (playlist != null) {
			playlist.loadTracks(this);
			setPrefsFeature.set(prefs.playlistName(), playlistName);
		}
		return playlist;
	}

	/**
	 * creates the playlist table
	 * 
	 * @param playlistTable
	 * @param playlist
	 *            the playlist to which the table will be build according to
	 */
	public void createPlaylistTable(FlexTable playlistTable, Playlist playlist) {
		playlistTable.removeAllRows();
		playlistTable.clear(true);
		playlistTable.getColumnFormatter().setWidth(0, "70%");
		int row = 0;
		if (playlist != null && playlist.getTracks() != null) {
			for (final Track track : playlist.getTracks()) {
				Label trackNameLabel = new Label(track.toString());
				trackNameLabel.setStyleName("standardLabel");

				playlistTable.setCellPadding(0);
				playlistTable.setCellSpacing(0);

				playlistTable.getRowFormatter().setStyleName(row,
						"flexTableRow");
				playlistTable.getRowFormatter().setVerticalAlign(row,
						HasVerticalAlignment.ALIGN_MIDDLE);

				playlistTable.setWidget(row, 0, trackNameLabel);
				playlistTable.getCellFormatter().setStyleName(row, 0,
						"flexTableRow");
				trackNameLabel.addClickHandler(new ClickHandler() {

					@Override
					public void onClick(ClickEvent event) {
						getCurrentPlaylist().setCurrentTrack(track);
						loadTrack(track, false);
					}
				});
				final Button menuButton = new Button();
				menuButton.setStyleName("menuButton");
				menuButton.addClickHandler(new ClickHandler() {
					@Override
					public void onClick(ClickEvent event) {
						final PopupPanel trackMenuPanel = new PopupPanel(true);
						trackMenuPanel
								.setAnimationEnabled(IS_ANIMATION_ENABLED);
						MenuBar trackMenu = new MenuBar(true);
						createTrackMenu(trackMenu, trackMenuPanel, track);
						FocusPanel focusPanel = new FocusPanel(trackMenu);
						focusPanel.addMouseOutHandler(new MouseOutHandler() {

							@Override
							public void onMouseOut(MouseOutEvent event) {
								trackMenuPanel.hide();
							}
						});
						trackMenuPanel.add(focusPanel);
						trackMenuPanel.showRelativeTo(menuButton);
					}
				});
				playlistTable.setWidget(row, 1, menuButton);
				playlistTable.getCellFormatter().setAlignment(row, 1,
						HasHorizontalAlignment.ALIGN_CENTER,
						HasVerticalAlignment.ALIGN_MIDDLE);
				playlistTable.getCellFormatter().setStyleName(row, 1,
						"flexTableRow");

				RatingViewer ratingViewer = new RatingViewer(this, track, false);
				playlistTable.setWidget(row, 2, ratingViewer);
				playlistTable.getCellFormatter().setAlignment(row, 2,
						HasHorizontalAlignment.ALIGN_CENTER,
						HasVerticalAlignment.ALIGN_MIDDLE);
				playlistTable.getCellFormatter().setStyleName(row, 2,
						"flexTableRow");

				Button deleteButton = new Button();
				deleteButton.setStyleName("deleteButton");
				deleteButton.addClickHandler(new ClickHandler() {

					@Override
					public void onClick(ClickEvent event) {
						deleteTrack(track);
					}
				});
				playlistTable.setWidget(row, 3, deleteButton);
				playlistTable.getCellFormatter().setAlignment(row, 3,
						HasHorizontalAlignment.ALIGN_RIGHT,
						HasVerticalAlignment.ALIGN_MIDDLE);
				playlistTable.getCellFormatter().setStyleName(row, 3,
						"flexTableRow");
				row++;
			}
		}
		handleTrackHighlights();
		setWidgetInPanel(playlistVerticalPanel, playlistsScrollPanel);
	}

	/**
	 * deletes a track
	 * 
	 * @param track
	 *            the track to delete
	 */
	private void deleteTrack(Track track) {
		dataStoreService.deleteTrack(viewer.getId(), track,
				new AsyncCallback<Void>() {

					@Override
					public void onSuccess(Void result) {
						loadPlaylist(getCurrentPlaylist().getName());
					}

					@Override
					public void onFailure(Throwable caught) {
						// TODO Auto-generated method stub

					}
				});
	}

	/**
	 * gets the current loaded playlist
	 * 
	 * @return the current playlist
	 */
	public Playlist getCurrentPlaylist() {
		return currentPlaylist;
	}

	public Playlist getPreviousPlaylist() {
		return previousPlaylist;
	}

	/**
	 * @param playlistTable
	 *            the playlistTable to set
	 */
	public void setPlaylistTable(FlexTable playlistTable) {
		this.playlistTable = playlistTable;
	}

	/**
	 * @return the playlistTable
	 */
	public FlexTable getPlaylistTable() {
		return playlistTable;
	}

	/**
	 * build a track menu
	 * 
	 * @param trackMenu
	 *            the menu to build
	 * @param popupPanel
	 */
	private void createTrackMenu(MenuBar trackMenu,
			final PopupPanel popupPanel, final Track track) {
		trackMenu.addItem("Rename", new Command() {

			@Override
			public void execute() {
				RenameTrackDialog dialog = new RenameTrackDialog(track,
						IPlayer.this);
				dialog.setPopupPosition(100, 200);
				popupPanel.hide();
				dialog.show();
			}
		});
		trackMenu.addItem("Move", new Command() {

			@Override
			public void execute() {
				MoveTrackDialog dialog = new MoveTrackDialog(track,
						IPlayer.this);
				dialog.setPopupPosition(100, 200);
				popupPanel.hide();
				dialog.show();
			}
		});
		trackMenu.addItem("Share", new Command() {

			@Override
			public void execute() {
				popupPanel.hide();
				people.newGetViewersFriendsRequestBuilder().setFields(
						Person.THUMBNAIL_URL, Person.ID, Person.DISPLAY_NAME)
						.build().execute(
								new Callback<OsapiCollection<Person>>() {

									@Override
									public void onSuccess(
											OsapiCollection<Person> object) {
										List<Person> friends = new ArrayList<Person>();
										for (Person friend : object.iterable()) {
											friends.add(friend);
										}
										SendTrackDialog dialog = new SendTrackDialog(
												viewer.getId(), friends, track);
										dialog.center();
									}

									@Override
									public void onFail(OsapiError error) {
										// TODO Auto-generated method stub

									}
								});
			}
		});
	}

	@Override
	public void initializeFeature(DynamicHeightFeature feature) {
		this.dynamicHeightFeature = feature;
	}

	@Override
	public void initializeFeature(OsapiFeature feature) {
		people = feature.getPeopleService();
	}

	/**
	 * @return the viewer
	 */
	public Person getViewer() {
		return viewer;
	}

	/**
	 * displays the top tracks in the given panel
	 * 
	 * @param panel
	 */
	private void displayTopTracks(final ScrollPanel panel, String type) {
		setWidgetInPanel(communityVerticalPanel, new Image(resources
				.loadingAnimation()));
		if (type.equalsIgnoreCase("World")) {
			displayWorldTopTracks(panel);
		} else if (type.equalsIgnoreCase("Friends")) {
			displayFriendsTopTracks(panel);
		}
	}

	/**
	 * @param panel
	 */
	private void displayWorldTopTracks(final ScrollPanel panel) {
		dataStoreService.getAllTracks(new AsyncCallback<List<Track>>() {

			@Override
			public void onSuccess(List<Track> result) {
				printTopTracks(panel, result);
			}

			@Override
			public void onFailure(Throwable caught) {
				// TODO Auto-generated method stub

			}
		});
	}

	private void displayFriendsTopTracks(final ScrollPanel panel) {
		people.newGetViewersFriendsRequestBuilder().setFields(
				Person.THUMBNAIL_URL, Person.ID, Person.DISPLAY_NAME).build()
				.execute(new Callback<OsapiCollection<Person>>() {

					@Override
					public void onSuccess(OsapiCollection<Person> object) {
						List<String> ids = new ArrayList<String>();
						ids.add(viewer.getId());
						for (Person friend : object.iterable()) {
							ids.add(friend.getId());
						}
						dataStoreService.getTracks(ids,
								new AsyncCallback<List<Track>>() {

									@Override
									public void onSuccess(List<Track> result) {
										printTopTracks(panel, result);
									}

									@Override
									public void onFailure(Throwable caught) {
										// TODO Auto-generated method stub

									}
								});
					}

					@Override
					public void onFail(OsapiError error) {
						// TODO Auto-generated method stub

					}
				});
	}

	/**
	 * @param panel
	 * @param result
	 */
	private void printTopTracks(final ScrollPanel panel, List<Track> result) {
		AverageRatingsCalculatorStrategy avgRatingsCalculator = new BayesianAverageRatingCalc();
		List<Track> tracks = avgRatingsCalculator.calcAvgRatings(result);
		FlexTable table = new FlexTable();
		table.setWidth("100%");
		table.setCellPadding(0);
		table.setCellSpacing(0);
		int row = 0;
		for (final Track track : tracks) {
			if (row < MAX_TOP_TRACKS) {
				table.getRowFormatter().setStyleName(row, "flexTableRow");
				table.setText(row, 0, (row + 1) + ".");
				Label trackLabel = new Label(track.toString());
				trackLabel.setStyleName("standardLabel");
				trackLabel.addClickHandler(new ClickHandler() {

					@Override
					public void onClick(ClickEvent event) {
						AddTrackDialog addTrackDialog = new AddTrackDialog(
								IPlayer.this);
						addTrackDialog.setTrack(new Track(track.getTitle(),
								track.getArtist(), track.getDuration(), track
										.getVideoID(), track.getThumbnailURL(),
								0));
						addTrackDialog.setPopupPosition(20, 150);
						addTrackDialog.show();
					}
				});
				table.setWidget(row, 1, trackLabel);
				RatingViewer ratingViewer = new RatingViewer(this, track, true);
				ratingViewer.setReadOnly(true);
				table.setWidget(row, 2, ratingViewer);
				table.getCellFormatter().setStyleName(row, 0, "flexTableRow");
				table.getCellFormatter().setStyleName(row, 1, "flexTableRow");
				table.getCellFormatter().setStyleName(row, 2, "flexTableRow");
				row++;
			} else {
				break;
			}

		}
		panel.setWidget(table);
		setWidgetInPanel(communityVerticalPanel, panel);
	}

	@Override
	public void initializeFeature(SetPrefsFeature feature) {
		setPrefsFeature = feature;
	}
}
